package com.qingzhuge.scheduler.listener;

import com.alibaba.fastjson.JSON;
import com.qingzhuge.common.concurrent.Executors;
import com.qingzhuge.common.email.Email;
import com.qingzhuge.common.email.EmailUtil;
import com.qingzhuge.common.utils.IpUtils;
import com.qingzhuge.common.utils.TimeUtil;
import com.qingzhuge.scheduler.entity.SysTaskRunLog;
import com.qingzhuge.scheduler.entity.SysTaskScheduled;
import com.qingzhuge.scheduler.enums.JobState;
import com.qingzhuge.scheduler.service.ISysTaskRunLogService;
import com.qingzhuge.scheduler.service.ISysTaskScheduledService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

import static com.qingzhuge.scheduler.entity.SysTaskScheduled.JOB_DATA_MAP_SCHEDULED;

/**
 * @author : zero.xiao
 * @description :
 * @date :2019-05-17 11:38
 * @modified :
 */
@Slf4j
@Component
public class JobListener implements org.quartz.JobListener {

    @Resource
    private ISysTaskRunLogService runLogService;
    @Resource
    private ISysTaskScheduledService scheduledService;
    /**
     * 线程池
     */
    private static ExecutorService executorService = Executors.newThreadPoolExecutor(JobListener.class.getName(), 2, 20, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(20));
    private static String JOB_LOG = "jobLog";

    @Override
    public String getName() {
        return "qingtingTaskListener";
    }

    /**
     * “否决JobDetail”是在Triiger被其相应的监听器监听时才具备的能力
     */
    @Override
    public void jobExecutionVetoed(JobExecutionContext context) {
        log.debug("{}:{}:被否决", getName(), context.getJobDetail().getJobClass());
    }

    /**
     * 任务开始前
     */
    @Override
    public void jobToBeExecuted(final JobExecutionContext context) {
        final JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        SysTaskScheduled scheduled = (SysTaskScheduled) jobDataMap.get(JOB_DATA_MAP_SCHEDULED);
        String targetObject = scheduled.getTargetObject();
        String targetMethod = scheduled.getTargetMethod();
        log.info("定时任务开始执行：{}.{}", targetObject, targetMethod);
        // 保存日志
        SysTaskRunLog log = new SysTaskRunLog();
        LocalDateTime fireTime = TimeUtil.date2LocalDateTime(context.getFireTime());
        log.setStartTime(fireTime);
        log.setGroupName(targetObject);
        log.setName(targetMethod);
        log.setStatus(JobState.INIT);
        log.setServerHost(IpUtils.getHostName());
        log.setScheduledId(scheduled.getId());
        scheduled.setFireTime(context.getFireTime());
        scheduled.setNextFireTime(context.getNextFireTime());
        scheduled.setPreviousFireTime(context.getPreviousFireTime());
        executorService.submit(() -> {
            try {
                scheduledService.save(scheduled);
                runLogService.save(log);
                jobDataMap.put(JOB_LOG, log);
            } catch (Exception e) {
                JobListener.log.error("save TaskRunLog cause error. The log object is : {}", log, e);
            }
        });
    }

    /**
     * 任务结束后
     */
    @Override
    public void jobWasExecuted(final JobExecutionContext context, JobExecutionException exp) {
        LocalDateTime end = TimeUtil.date2LocalDateTime(System.currentTimeMillis());
        final JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        SysTaskScheduled scheduled = (SysTaskScheduled) jobDataMap.get(JOB_DATA_MAP_SCHEDULED);
        String targetObject = scheduled.getTargetObject();
        String targetMethod = scheduled.getTargetMethod();
        log.info("定时任务执行结束：{}.{}", targetObject, targetMethod);
        // 更新任务执行状态
        final SysTaskRunLog log = (SysTaskRunLog) jobDataMap.get(JOB_LOG);
        if (log != null) {
            log.setEndTime(end);
            if (exp != null) {
                JobListener.log.error("定时任务失败: [" + targetObject + "." + targetMethod + "]", exp);
                String contactEmail = scheduled.getContactEmail();
                if (StringUtils.isNotBlank(contactEmail)) {
                    String topic = String.format("调度[%s.%s]发生异常", targetMethod, targetMethod);
                    sendEmail(new Email(contactEmail, topic, exp.getMessage()));
                }
                log.setStatus(JobState.ERROR);
                log.setFireInfo(exp.getMessage());
            } else {
                if (log.getStatus().equals(JobState.INIT)) {
                    log.setStatus(JobState.SUCCESS);
                }
            }
        }
        executorService.submit(() -> {
            if (log != null) {
                try {
                    runLogService.save(log);
                } catch (Exception e) {
                    JobListener.log.error("Update TaskRunLog cause error. The log object is : " + JSON.toJSONString(log), e);
                }
            }
        });
    }

    private void sendEmail(final Email email) {
        executorService.submit(() -> {
            //TODO 可以通过MQ发送
            log.info("将发送邮件至：" + email.getSendTo());
            EmailUtil.sendEmail(email);
        });
    }
}
